package com.zzh.utils;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
import redis.clients.jedis.commands.JedisCommands;
import redis.clients.jedis.commands.MultiKeyCommands;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

//import com.thirtydays.account.model.vo.account.AccountLoginContext;

/**
 * 
 * Copyright: Copyright (c) 2020 mosc
 * @ClassName: CacheService.java
 * @Description [功能描述]
 * @version: v1.0.0
 * @author mosc
 * @CreateDate 2020年4月10日 上午11:25:56 
 * @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述]
 */
@Service
public class RedisUtils {

	@Resource(name = "redisTemplate")
	private RedisTemplate<String, Object> template;

	public void set(String key, Object value) {
		template.opsForValue().set(key, value);
	}

	public <T> T get(String key,Class<T> classType) {
		Object data = template.opsForValue().get(key);
		return (T)data;
	}

	public Object get(String key){
		return template.opsForValue().get(key);
	}

	/**
	 * 根据key删除
	 * @param key
	 */
	public void del(String key){
		template.delete(key);
	}

	public Integer incr(String key, int increment) {
		return template.opsForValue().increment(key, increment).intValue();
	}

	public Integer incr(String key, int increment, int expire, TimeUnit timeUnit) {
		Integer value = template.opsForValue().increment(key, increment).intValue();
		template.expire(key, expire, timeUnit);
		return value;
	}

	public void evictKey(String key) {
		template.delete(key);
	}
	
	public boolean setNxWithExpireTime(String key, Object value,long expireTime, TimeUnit timeUnit) {
		return template.opsForValue().setIfAbsent(key, value, expireTime, timeUnit);
    }

	public void setWithExpireTime(String key, Object value,long expireTime, TimeUnit timeUnit)
    {
		template.opsForValue().set(key, value, expireTime, timeUnit);;
    }

	public void hset(String hashKey, String key, Object value)
	{
		template.boundHashOps(hashKey).put(key, value);
	}

	public Integer hincr(String hashKey, String key, long value) {
		return template.boundHashOps(hashKey).increment(key, value).intValue();
	}
	
	public Integer hincrWithExpire(String hashKey, String key, long value, long ttlSecond) {
		Long incrValue = template.boundHashOps(hashKey).increment(key, value);
		template.boundHashOps(hashKey).expire(ttlSecond, TimeUnit.SECONDS);
		return incrValue.intValue();
	}

	/**
	 * hash 设置 过期
	 * @param hashKey
	 * @param key
	 * @param value
	 * @param ttlSecond
	 * @param timeUnit
	 * @return
	 */
	public void hashWithExpire(String hashKey, String key, Object value, long ttlSecond,TimeUnit timeUnit) {
		template.boundHashOps(hashKey).put(key, value);
		template.boundHashOps(hashKey).expire(ttlSecond, timeUnit);
	}

	public void hsetWithExpire(String hashKey, String key, Object value, long ttlSecond) {
		template.boundHashOps(hashKey).put(key, value);
		template.boundHashOps(hashKey).expire(ttlSecond, TimeUnit.SECONDS);
	}

	public Object hget(String hashKey, String key) {
		return template.boundHashOps(hashKey).get(key);
	}

	public void hdel(String hashKey, String key) {
		template.boundHashOps(hashKey).delete(key);
	}

	public void hdelAll(String hashKey) {
		template.delete(hashKey);
	}

	public Map<Object, Object> hgetAll(String hashKey) {
		return template.boundHashOps(hashKey).entries();
	}
	
	/**
	 * @Function sadd
	 * @Description 
	 * [
	 *  	可用版本： >= 1.0.0
	 *  	时间复杂度: O(N)， N 是被添加的元素的数量。
	 *		将一个或多个 member 元素加入到集合 key 当中，已经存在于集合的 member 元素将被忽略。
	 *		假如 key 不存在，则创建一个只包含 member 元素作成员的集合。当 key 不是集合类型时，返回一个错误。
	 *	
	 *		返回值
	 *		添加成功或失败
	 *	]
	 * @param setKey
	 * @param value
	 * @return
	 * @Return boolean
	 * @version: v1.0.0
	 * @author mosc
	 * @CreateDate 2020年10月22日 下午2:32:17 
	 * @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述]     
	 */
	public boolean sadd(String setKey, String value) {
		Long addedNum = template.boundSetOps(setKey).add(value);
		return addedNum > 0;
	}
	
	/**
	 * @Function srem
	 * @Description [移除集合中的某个元素]
	 * @param setKey
	 * @param value
	 * @return
	 * @Return boolean
	 * @version: v1.0.0
	 * @author mosc
	 * @CreateDate 2020年10月22日 下午3:31:49 
	 * @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述]     
	 */
	public boolean srem(String setKey, String value) {
		Long removeNum = template.boundSetOps(setKey).remove(value);
		return removeNum > 0;
	}

	public int zSetIncrement(String zsetKey,Object key,Double delta){
		Double value = template.opsForZSet().incrementScore(zsetKey, key, delta);
		return value.intValue();
	}
	public int zSetScore(String zsetKey,Object key){
		Double value = template.opsForZSet().score(zsetKey, key);
		return value.intValue();
	}
	
	public Cursor<ZSetOperations.TypedTuple<Object>> zSetScan(String zsetKey){
		return template.opsForZSet().scan(zsetKey, ScanOptions.NONE);
	}
	
	public Set<ZSetOperations.TypedTuple<Object>> zSetReverseRange(String zsetKey,Long start,Long end)
	{
		return template.opsForZSet().reverseRangeByScoreWithScores(zsetKey, start, end);
	}
	
	/**
	 * 
	 * @Function lockWithExpireNew
	 * @Description [加锁以及设置正确的有效期]
	 * @param lockKey
	 * @param expire
	 * @return
	 * @Return boolean
	 * @version: v1.0.0
	 * @author mosc
	 * @CreateDate 2020年4月14日 下午2:27:44 
	 * @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述]
	 */
	public boolean lockWithExpireNew(String lockKey, int expire)
	{

		int value = template.opsForValue().increment(lockKey, 1).intValue();
		
		if(value == 1) {
			template.expire(lockKey, expire, TimeUnit.SECONDS);
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * 
	 * @Function releaseLock
	 * @Description [释放锁（新版）]
	 * @param lockKey
	 * @Return void
	 * @version: v1.0.0
	 * @author mosc
	 * @CreateDate 2020年4月14日 下午2:29:39 
	 * @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述]
	 */
	public void releaseLockNew(String lockKey)
	{
		Integer value = get(lockKey,Integer.class);
		if(null != value)
		{
			evictKey(lockKey);
		}
	}

	public long generate(String key) {
		RedisAtomicLong counter = new RedisAtomicLong(key, template.getConnectionFactory());
		return counter.incrementAndGet();
	}

	/**
	 * 校验短信验证码
	 *
	 * @param validateCodeKey 手机号码或邮箱 的 redis key
	 *        String validateCodeKey = String.format(CacheKeyPrefixConst.LOGIN_VALIDATE_CODE, verifyParam);
	 * @param validateCode 验证码
	 * @return
	 * @return boolean 校验结果，true为成功，false为失败
	 * @author caiwenhao
	 * @date 2020年3月29日 下午4:25:37
	 */
	public boolean checkValidateCode(String validateCodeKey, String validateCode) {
		// 获取发送的短信验证码
		String validateCodeTemp = this.get(validateCodeKey, String.class);
		if (StringUtils.isBlank(validateCodeTemp)) {
			return false;
		}

		return validateCodeTemp.equals(validateCode);
	}
	/**
	 * @Description : scan 扫描
	 * @param key :
	 * @Author : ant
	 * @Return : java.util.Set<java.lang.String>
	 * @Date : 2020/12/28 12:18
	 **/
	public Set<String> scan(String key)
	{
		return template.execute((RedisCallback<Set<String>>) connection -> {
			Set<String> keys = Collections.
					newSetFromMap(new ConcurrentHashMap<>(16));
			JedisCommands commands = (JedisCommands) connection.getNativeConnection();
			MultiKeyCommands multiKeyCommands = (MultiKeyCommands) commands;
			ScanParams scanParams = new ScanParams();
			scanParams.match(key + "*");
			scanParams.count(1000);
			ScanResult<String> scan = multiKeyCommands.scan("0", scanParams);
			while (null != scan.getCursor()) {
				keys.addAll(scan.getResult());
				if (!StringUtils.equals("0", scan.getCursor())) {
					scan = multiKeyCommands.scan(scan.getCursor(), scanParams);
					continue;
				} else {
					break;
				}
			}
			return keys;
		});
	}
}
